﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using USMCdotNET;

namespace USMC.Net
{
    public delegate void PositionChangedEvent(object sender,PositionChangedEventArgs e);
    /// <summary>
    /// Логика взаимодействия для PointControl.xaml
    /// </summary>
    public partial class PointControl : UserControl
    {
        static int pointNumber=0;
        int currentNumber;
        public PointControl(Func<double, double> posXToSteps, Func<double, double> posYToSteps, Func<double, double> stepsXToPos, Func<double, double> stepsYToPos, PointParameters parameters)
        {
            InitializeComponent();
            positionXToSteps = posXToSteps;
            positionYToSteps = posYToSteps;
            stepsXToPosition = stepsXToPos;
            stepsYToPosotion = stepsYToPos;
            this.Width = 10;
            this.Height = 10;
            Parameters = parameters;
            XSteps = parameters.StepX;
            YSteps = parameters.StepY;
            NextInStack = this;
            currentNumber = pointNumber;
            pointNumber++;
        }
        #region События
        event PositionChangedEvent positionChanged;
        event Action removed;
        /// <summary>
        /// событие, выбрасываемое при удалении данной точки
        /// </summary>
        public event Action Removed
        {
            add
            {
                removed += value;
            }
            remove
            {
                removed -= value;
            }
        }
        /// <summary>
        /// удаление текущей точки
        /// </summary>
        public void Remove()
        {
            if (removed != null)
                removed();
        }
        /// <summary>
        /// событие, зажигаемое в случае, если позиция точки была изменена
        /// </summary>
        public event PositionChangedEvent PositionChanged
        {
            add
            {
                positionChanged += value;
            }
            remove
            {
                positionChanged -= value;
            }
        }
        #endregion
        #region ссылки на следующий и предыдущий элементы
        PointControl prevPoint;
        PointControl nextPoint;
        public PointControl PrevPoint
        {
            get{ return prevPoint;}
            set{ prevPoint = value;}
        }
        public PointControl NextPoint
        {
            get{return nextPoint;}
            set{nextPoint=value;}
        }
        //циплический односвязный список точек с одинаковыми координатами
        public PointControl NextInStack
        { get; set; }
        #endregion
        #region Координаты устройства и соответствующее положение на канве
        //конвертеры позиции на канве в координаты устройства 
        Func<double, double> positionXToSteps;
        Func<double, double> positionYToSteps;
        //конвертеры координат устройства в позицию на канве
        Func<double, double> stepsXToPosition;
        Func<double, double> stepsYToPosotion;
        //процедуры корректировки координат
        Action<bool> correctYStep;
        Action<bool> correctXStep;
        /// <summary>
        /// Координата X на канве
        /// </summary>
        public double X
        {
            get
            {
                return Canvas.GetLeft(this)+this.Width/2*LayoutTransform.Value.M11;
            }
            set
            {
                double d = value;
                Canvas.SetLeft(this, d - Width / 2 * LayoutTransform.Value.M11);
                Parameters.StepX = Math.Round(positionXToSteps(d)*Parameters.StepDivisor);
                if (UpdatePreviousShape != null)
                    UpdatePreviousShape();
                if (UpdateNextShape != null)
                    UpdateNextShape();
                if (positionChanged != null)
                    positionChanged(this, new PositionChangedEventArgs(new Point(d, Canvas.GetTop(this) + this.Width / 2 * LayoutTransform.Value.M11)));
            }
        }
        /// <summary>
        /// Координата Y на канве
        /// </summary>
        public double Y
        {
            get
            {
                return Canvas.GetTop(this) + this.Height / 2 * LayoutTransform.Value.M22;
            }
            set
            {
                double d = value;
                Canvas.SetTop(this, d - Height / 2 * LayoutTransform.Value.M22);
                Parameters.StepY = Math.Round(positionYToSteps(d) * Parameters.StepDivisor);
                if (UpdatePreviousShape != null)
                    UpdatePreviousShape();
                if (UpdateNextShape != null)
                    UpdateNextShape();
                if (positionChanged != null)
                    positionChanged(this, new PositionChangedEventArgs(new Point(Canvas.GetLeft(this) + this.Width / 2 * LayoutTransform.Value.M22, d)));
            }
        }
        /// <summary>
        /// Координата X устройства
        /// </summary>
        public double XSteps
        {
            get
            {
                return Parameters.StepX;
            }
            set
            {
                Parameters.StepX = Math.Round(value); 
                double d = stepsXToPosition(Parameters.StepX / Parameters.StepDivisor);
                Canvas.SetLeft(this, d - this.Width / 2 * this.LayoutTransform.Value.M11);
                if (UpdatePreviousShape != null)
                    UpdatePreviousShape();
                if (UpdateNextShape != null)
                    UpdateNextShape();
                if (positionChanged != null)
                    positionChanged(this, new PositionChangedEventArgs(new Point(Canvas.GetLeft(this) + Width / 2 * this.LayoutTransform.Value.M11, d)));
            }
        }
        /// <summary>
        /// Координата Y устройства
        /// </summary>
        public double YSteps
        {
            get
            {
                return Parameters.StepY;
            }
            set
            {
                Parameters.StepY = Math.Round(value);
                double d = stepsYToPosotion(Parameters.StepY / Parameters.StepDivisor);
                Canvas.SetTop(this, d - this.Height / 2 * this.LayoutTransform.Value.M22);
                if (UpdatePreviousShape != null)
                    UpdatePreviousShape();
                if (UpdateNextShape != null)
                    UpdateNextShape();
                if (positionChanged != null)
                    positionChanged(this, new PositionChangedEventArgs(new Point(Canvas.GetLeft(this) + Height / 2 * this.LayoutTransform.Value.M22, d)));
            }
        }
        public void CorrectCoordinates(bool showMessageBox)
        {
            if (correctXStep != null)
                correctXStep(showMessageBox);
            if (correctYStep != null)
                correctYStep(showMessageBox);
        }
        #endregion
        #region Параметры
        public PointParameters Parameters;
        public int Number
        {
            get { return currentNumber; }
        }
        LinkedList<CycleInfo> cycleCollection = new LinkedList<CycleInfo>();
        public LinkedList<CycleInfo> Cycles
        {
            get { return cycleCollection; }
        }
        public void ClearCycles()
        {
            foreach (CycleInfo cycle in cycleCollection)
            {
                if (cycle.CycleStart == this)
                    cycle.CycleEnd.cycleCollection.Remove(cycle);
                else
                    cycle.CycleStart.cycleCollection.Remove(cycle);
            }
            cycleCollection.Clear();
        }
        public void AddCycle(CycleInfo cycle)
        {
            if (cycleCollection.First == null)
            {
                cycleCollection.AddFirst(cycle);
            }
            else
            {
                LinkedListNode<CycleInfo> point = cycleCollection.First;
                if (cycle.CycleEnd == this)
                {
                    while (point != null && point.Value.CycleEnd == this && point.Value.CycleStart.Number < cycle.CycleStart.Number)
                        point = point.Next;
                }
                else
                {
                    while (point!=null && point.Value.CycleStart != this)
                        point = point.Next;
                    while (point != null && point.Value.CycleEnd.Number < cycle.CycleEnd.Number)
                        point = point.Next;
                }
                if (point == null)
                    cycleCollection.AddLast(cycle);
                else
                    cycleCollection.AddBefore(point, cycle);
            }
        }
        public void RemoveCycle(CycleInfo cycle)
        {
            try
            {
                cycleCollection.Remove(cycle);
            }
            catch (InvalidOperationException)
            {
            }
        }
        public IEnumerator<CycleInfo> GetCycleEnumerator()
        {
            return cycleCollection.GetEnumerator();
        }
        public LinkedListNode<CycleInfo> FirstCycleNode
        {
            get
            {
                if (cycleCollection.Count == 0)
                    return null;
                else
                    return cycleCollection.First;
            }
        }
        #endregion
        #region Выделение и внешний вид
        bool selected = false;
        public bool IsSelected
        {
            get { return selected; }
            set
            {
                if (value)
                {
                    rect.SetResourceReference(Rectangle.StrokeProperty, "selectionstrokeBrush");
                    rect.StrokeThickness = 2;
                }
                else
                {
                    rect.SetResourceReference(Rectangle.StrokeProperty, "strokeBrush");
                    rect.StrokeThickness = 1;
                }
                selected = value;
                prevShape.Stroke = rect.Stroke;
            }
        }
        public new double Opacity
        {
            get { return rect.Opacity; }
            set { rect.Opacity = value; }
        }
        public void UpdateCoordinates()
        {
            XSteps = Parameters.StepX;
            YSteps = Parameters.StepY;
        }
        public Color Color
        {
            set
            {
                rect.Fill = new SolidColorBrush(value);
            }
        }
        #region Фигуры

        Shape nextShape, prevShape;
        object par;
        public object ShapeParameters
        {
            get
            {return par;}
        }
        public void AssignShapeParameters(ValueType parameters)
        {
            par = parameters;
        }
        public object GetShapeParameters()
        {
            return par;
        }
        public Shape NextShape
        {
            get { return nextShape; }
            set 
            {
                nextShape = value;
                if (nextShape != null)
                {
                    if (nextShape is Line)
                    {
                        Line line = nextShape as Line;
                        UpdateNextShape = () =>
                        {
                            line.X1 = X;
                            line.Y1 = Y;
                        };
                    }
                    else if (nextShape is Rectangle)
                    {
                        Rectangle rect = nextShape as Rectangle;
                        UpdateNextShape =
                        () =>
                        {
                            if (nextPoint.X < X)
                            {
                                rect.Width = X - nextPoint.X;
                                Canvas.SetLeft(rect, nextPoint.X);
                            }
                            else
                            {
                                rect.Width = nextPoint.X - X;
                                Canvas.SetLeft(rect, X);
                            }
                            if (nextPoint.Y < Y)
                            {
                                rect.Height = Y - nextPoint.Y;
                                Canvas.SetTop(rect, nextPoint.Y);
                            }
                            else
                            {
                                rect.Height = nextPoint.Y - Y;
                                Canvas.SetTop(rect, Y);
                            }
                        };
                        correctYStep = new Action<bool>(CorrectYStepWithRectangle);
                        correctXStep = new Action<bool>(CorrectXStepWithRectangle);
                    }
                    UpdateNextShape();
                }
                else
                {
                    UpdateNextShape = null;
                    if (!(prevShape is Rectangle))
                    {
                        correctYStep = null;
                        correctXStep = null;
                    }
                }
            }
        }

        public Shape PreviousShape
        {
            get { return prevShape; }
            set
            {
                prevShape = value;
                if (prevShape != null)
                {
                    if (prevShape is Line)
                    {
                        Line line = prevShape as Line;
                        UpdatePreviousShape = () =>
                        {
                            line.X2 = X;
                            line.Y2 = Y;
                        };
                        //if (!(prevShape is Rectangle) && !(nextShape is Rectangle))
                        //{
                        //    correctXStep = null;
                        //    correctYStep = null;
                        //}
                    }
                    else if (prevShape is Rectangle)
                    {
                        Rectangle rect = prevShape as Rectangle;
                        UpdatePreviousShape =
                        () =>
                        {
                            if (X < PrevPoint.X)
                            {
                                rect.Width = PrevPoint.X - X;
                                Canvas.SetLeft(rect, X);
                            }
                            else
                            {
                                rect.Width = X - PrevPoint.X;
                                Canvas.SetLeft(rect, PrevPoint.X);
                            }
                            if (Y < PrevPoint.Y)
                            {
                                rect.Height = PrevPoint.Y - Y;
                                Canvas.SetTop(rect, Y);
                            }
                            else
                            {
                                rect.Height = Y - PrevPoint.Y;
                                Canvas.SetTop(rect, PrevPoint.Y);
                            }
                        };
                        correctYStep = new Action<bool>(CorrectYStepWithRectangle);
                        correctXStep = new Action<bool>(CorrectXStepWithRectangle);
                    }
                    UpdatePreviousShape();
                }
                else
                {
                    UpdatePreviousShape = null;
                    if (!(nextShape is Rectangle))
                    {
                        correctYStep = null;
                        correctXStep = null;
                    }
                }
            }
        }
        //обновление фигур при смене координат
        public Action UpdatePreviousShape
        {
            get;
            set;
        }

        public Action UpdateNextShape
        {
            get;
            set;
        }
        private void CorrectYStepWithRectangle(bool showMessageBox)
        {
            RectangleParameters rectPar;
                PointControl point;
                PointControl changingPoint;
                if (prevShape is Rectangle)
                {
                    point = prevPoint;
                    changingPoint = this;
                    rectPar = (RectangleParameters)par;
                }
                else
                {
                    point = this;
                    changingPoint = nextPoint;
                    rectPar = (RectangleParameters)nextPoint.par;
                }
                if (!rectPar.Dir)
                {
                    int shift = (int)(changingPoint.Parameters.StepY - point.YSteps) % rectPar.Shift;
                    if (shift != 0)
                    {
                        //if (showMessageBox)
                        //    MessageBox.Show("Текущие значения высоты и сдвига несовместимы. Высота будет изменена так, чтобы в ней умещалось целое количество сдвигов");
                        changingPoint.Parameters.StepY = changingPoint.Parameters.StepY -= shift;
                    }
                }
        }
        private double CorrectYCoordWithRectangle(double d)
        {
            double steps = Math.Round(positionYToSteps(d) * Parameters.StepDivisor);
            PointControl point;
            if (prevShape is Rectangle)
                point = prevPoint;
            else
                point = nextPoint;
            if ((steps - point.YSteps) % 2 == 0)
            {
                if (steps > point.YSteps)
                    steps = steps - 1;
                else
                    steps = steps + 1;
            }
            return stepsYToPosotion(steps);
        }
        private void CorrectXStepWithRectangle(bool showMessageBox)
        {
            RectangleParameters rectPar;
            PointControl point;
            PointControl changingPoint;
            if (prevShape is Rectangle)
            {
                point = prevPoint;
                changingPoint = this;
                rectPar= (RectangleParameters)par;
            }
            else
            {
                point = this;
                changingPoint = nextPoint;
                rectPar = (RectangleParameters)nextPoint.par;
            }
            if (rectPar.Dir)
            {
                int shift = (int)(changingPoint.Parameters.StepX - point.XSteps) % rectPar.Shift;
                if (shift != 0)
                {
                  //  if (showMessageBox)
                  //      MessageBox.Show("Текущие значения ширины и сдвига несовместимы. Ширина будет изменена так, чтобы в ней умещалось целое количество сдвигов");
                    changingPoint.Parameters.StepX = changingPoint.Parameters.StepX -= shift;
                }
            }
            
        }
        private double CorrectXCoordWithRectangle(double d)
        {
            double steps = Math.Round(positionXToSteps(d) * Parameters.StepDivisor);
            if (prevShape is Rectangle)
                if (steps - prevPoint.XSteps == 0)
                    steps = steps + 1;
            if (nextShape is Rectangle)
                if (steps - nextPoint.XSteps == 0)
                    steps = steps + 1;

            return stepsXToPosition(steps);
        }
        #endregion
        #endregion
        public override string ToString()
        {
            return X.ToString() + " " + Y.ToString();
        }
    }
}
